// <summary>
// 一个正在运行中的流程实例
//</summary>
// ***********************************************************************

using Magic.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
namespace Magic.FlowCenter.Entity;

/// <summary>
/// 流程运行类
/// </summary>
	public class FlowRuntime
{
    /// <summary>
    /// 构造函数
    /// </summary>
    public FlowRuntime(FlcFlowinstance instance)
    {
        var schemeContentJson = instance.SchemeContent.ToObject<Flow>();//获取工作流模板内容的json对象;
        InitFlow(schemeContentJson);

        currentNodeId = (string.IsNullOrEmpty(instance.ActivityId)? startNodeId : instance.ActivityId);
        currentNodeType = GetNodeType(currentNodeId);
        FrmData = instance.FrmData;
        previousId = GetPreviousNodeId(currentNodeId);
        flowInstanceId = instance.Id;

        //会签开始节点和流程结束节点没有下一步
        if (currentNodeType == 0 || currentNodeType == 4)
        {
            nextNodeId = "-1";
            nextNodeType = -1;
        }
        else
        {
            nextNodeId = GetNextNodeId();//下一个节点
            nextNodeType = GetNodeType(nextNodeId);
        }
    }
    #region 私有方法

    /// <summary>
    /// 获取工作流信息
    /// </summary>
    /// <param name="schemeContentJson"></param>
    /// <returns></returns>
    private void InitFlow(Flow schemeContentJson)
    {
        flow = schemeContentJson;
        InitLines(schemeContentJson);
        InitNodes(schemeContentJson);
    }
    /// <summary>
    /// 获取工作流节点的字典列表:key节点id
    /// </summary>
    /// <param name="schemeContentJson"></param>
    /// <returns></returns>
    private void InitNodes(Flow schemeContentJson)
    {
        Nodes = new Dictionary<string, FlowNode>();
        foreach (var item in schemeContentJson.nodeList)
        {
            var node = item;
            if (!Nodes.ContainsKey(node.id))
            {
                Nodes.Add(node.id, node);
            }
            if (node.type == FlowNode.START)
            {
                this.startNodeId = node.id;
            }
        }
    }

    private void InitLines(Flow schemeContentJson)
    {
        Lines = new List<FlowLine>();
        FromNodeLines = new Dictionary<string, List<FlowLine>>();
        ToNodeLines = new Dictionary<string, List<FlowLine>>();
        foreach (var item in schemeContentJson.linkList)
        {
            var line = item;
            Lines.Add(line);

            if (!FromNodeLines.ContainsKey(line.sourceId))
            {
                List<FlowLine> d = new List<FlowLine> { line };
                FromNodeLines.Add(line.sourceId, d);
            }
            else
            {
                FromNodeLines[line.sourceId].Add(line);
            }

            if (!ToNodeLines.ContainsKey(line.targetId))
            {
                List<FlowLine> d = new List<FlowLine> { line };
                ToNodeLines.Add(line.targetId, d);
            }
            else
            {
                ToNodeLines[line.targetId].Add(line);
            }
        }
    }

    /// <summary>
    /// 获取下一个节点
    /// </summary>
    private string GetNextNodeId(string nodeId = null)
    {
        var lines = nodeId == null ? FromNodeLines[currentNodeId] : FromNodeLines[nodeId];
        if (lines.Count == 0)
        {
            throw new Exception("无法寻找到下一个节点");
        }

        if (string.IsNullOrEmpty(FrmData)) return lines[0].targetId;

        FrmData = FrmData.ToLower();//统一转小写
        var frmDataJson = FrmData.ToJObject();//获取数据内容

        foreach (var l in lines)
        {
            if (l.Compares==null)
            {
                l.Compares = new List<DataCompare>();
            }
            if (l.Compares.Count>0 &&l.Compare(frmDataJson))
            {
                return l.targetId;
            }
        }

        return lines[0].targetId;
    }
    /// <summary>
    /// 获取上一个节点
    /// </summary>
    private string GetPreviousNodeId(string nodeId = null)
    {
        try
        {
            var lines = nodeId == null ? ToNodeLines[currentNodeId] : ToNodeLines[nodeId];
            if (lines.Count == 0)
            {
                return string.Empty;
            }

            if (FrmData == "") return lines[0].sourceId;

            FrmData = FrmData.ToLower();//统一转小写
            var frmDataJson = FrmData.ToJObject();//获取数据内容

            foreach (var l in lines)
            {
                if (l.Compares == null)
                {
                    l.Compares = new List<DataCompare>();
                }
                if (l.Compares.Count > 0 && l.Compare(frmDataJson))
                {
                    return l.sourceId;
                }
            }

            return lines[0].sourceId;
        }
        catch (Exception)
        {
            return string.Empty;
        }

    }
    #endregion 私有方法

    #region 共有方法

    /// <summary>
    /// 获取下一个节点
    /// </summary>
    /// <param name="nodeId"></param>
    /// <returns></returns>
    public FlowNode GetNextNode(string nodeId = null)
    {
        return Nodes[GetNextNodeId(nodeId)];
    }

    /// <summary>
    /// 获取实例接下来运行的状态
    /// </summary>
    /// <returns>-1无法运行,0会签开始,1会签结束,2一般节点,4流程运行结束</returns>
    public int GetNextNodeType()
    {
        if (nextNodeId != "-1")
        {
            return GetNodeType(nextNodeId);
        }
        return -1;
    }

    /// <summary>
    /// 获取节点类型 0会签开始,1会签结束,2一般节点,开始节点,4流程运行结束
    /// </summary>
    /// <param name="nodeId"></param>
    /// <returns></returns>
    public int GetNodeType(string nodeId)
    {
        switch (Nodes[nodeId].type)
        {
            //会签开始节点
            case FlowNode.FORK:
                return 0;
            //会签结束节点
            case FlowNode.JOIN:
                return 1;
            //结束节点
            case FlowNode.END:
                return 4;
            //开始节点
            case FlowNode.START:
                return 3;

            default:
                return 2;
        }
    }
    /// <summary>
    /// 删除节点
    /// </summary>
    /// <param name="nodeId"></param>
    public void RemoveNode(string nodeId)
    {
        var node = Nodes[nodeId];
        if (node != null && node.setInfo != null)
        {
            node.setInfo.Taged = null;
            node.setInfo.UserName = null;
            node.setInfo.UserId = null;
            node.setInfo.Description = null;
            node.setInfo.TagedTime = null;
        }
    }
    /// <summary>
    /// 删除全部节点
    /// </summary>
    public void RemoveNodes()
    {
        foreach (var item in Nodes)
        {
            var node = item.Value;
            if (node != null && node.setInfo != null)
            {
                node.setInfo.Taged = null;
                node.setInfo.UserName = null;
                node.setInfo.UserId = null;
                node.setInfo.Description = null;
                node.setInfo.TagedTime = null;
            }
        }
    }

    /// <summary>
    /// 节点会签审核
    /// </summary>
    /// <param name="nodeId">会签时，currentNodeId是会签开始节点。这个表示当前正在处理的节点</param>
    /// <param name="tag"></param>
    /// <returns>-1不通过,1等待,其它通过</returns>
    public string NodeConfluence(string nodeId, Tag tag)
    {
        var forkNode = Nodes[currentNodeId];  //会签开始节点
        FlowNode nextNode = GetNextNode(nodeId); //获取当前处理的下一个节点

        int forkNumber = FromNodeLines[currentNodeId].Count;   //直接与会签节点连接的点，即会签分支数目
        string res =string.Empty;  //记录会签的结果,默认正在会签
        if (forkNode.setInfo.NodeConfluenceType == "one") //有一个步骤通过即可
        {
            if (tag.Taged == (int) TagState.Ok)
            {
                if (nextNode.type == FlowNode.JOIN)  //下一个节点是会签结束，则该线路结束
                {
                    res = GetNextNodeId(nextNode.id);
                }
            }
            else if(tag.Taged ==(int) TagState.No)
            {
                if (forkNode.setInfo.ConfluenceNo == null)
                {
                    forkNode.setInfo.ConfluenceNo = 1;
                }
                else if (forkNode.setInfo.ConfluenceNo == (forkNumber - 1))
                {
                    res = TagState.No.ToString("D");
                }
                else
                {
                    bool isFirst = true;  //是不是从会签开始到现在第一个
                    var preNode = GetPreNode(nodeId);
                    while (preNode.id != forkNode.id) //反向一直到会签开始节点
                    {
                        if (preNode.setInfo != null && preNode.setInfo.Taged == (int) TagState.No)
                        {
                            isFirst = false;
                            break;
                        }
                    }

                    if (isFirst)
                    {
                        forkNode.setInfo.ConfluenceNo++;
                    }
                }
            }
        }
        else //默认所有步骤通过
        {
            if (tag.Taged == (int) TagState.No)  //只要有一个不同意，那么流程就结束
            {
                res = TagState.No.ToString("D");
            }
            else if(tag.Taged == (int)TagState.Ok)
            {
                if (nextNode.type == FlowNode.JOIN)  //这种模式下只有坚持到【会签结束】节点之前才有意义，是否需要判定这条线所有的节点都通过，不然直接执行这个节点？？
                {
                    if (forkNode.setInfo.ConfluenceOk == null)
                    {
                        forkNode.setInfo.ConfluenceOk = 1;
                    }
                    else if (forkNode.setInfo.ConfluenceOk == (forkNumber - 1))  //会签成功
                    {
                        res = GetNextNodeId(nextNode.id);
                    }
                    else
                    {
                        forkNode.setInfo.ConfluenceOk++;
                    }
                }
            }
        }

        if (res == TagState.No.ToString("D"))
        {
            tag.Taged = (int) TagState.No;
            MakeTagNode(nextNode.id, tag);
        }
        else if (!string.IsNullOrEmpty(res)) //会签结束，标记合流节点
        {
            tag.Taged = (int) TagState.Ok;
            MakeTagNode(nextNode.id, tag);
            nextNodeId = res;
            nextNodeType = GetNodeType(res);
        }
        else
        {
            nextNodeId = nextNode.id;
            nextNodeType = GetNodeType(nextNode.id);
        }
        return res;
    }

    /// <summary>
    /// 获取上一个节点
    /// </summary>
    /// <param name="nodeId"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    private FlowNode GetPreNode(string nodeId = null)
    {
        var lines = nodeId == null ? ToNodeLines[currentNodeId] : ToNodeLines[nodeId];
        if (lines.Count == 0)
        {
            throw new Exception("无法找到上一个点");
        }
        return Nodes[lines[0].sourceId];
    }

    /// <summary>
    /// 驳回
    /// </summary>
    /// <param name="rejectType">驳回类型。null:使用节点配置的驳回类型/0:前一步/1:第一步/2：指定节点，使用NodeRejectStep</param>
    /// <returns></returns>
    public string RejectNode(string rejectType)
    {
        dynamic node = Nodes[currentNodeId];
        if (node.setInfo != null && string.IsNullOrEmpty(rejectType))
        {
            rejectType = node.setInfo.NodeRejectType;
        }
        
        if (rejectType == "0")
        {
            return previousId;
        }
        if (rejectType == "1")
        {
            return startNodeId;
        }
        return previousId;
    }

    ///<summary>
    /// 标记节点1通过，2不通过，3驳回
    /// </summary>
    /// <param name="nodeId"></param>
    public void MakeTagNode(string nodeId, Tag tag)
    {
        foreach (var item in Nodes)
        {
            if (item.Key == nodeId)
            {
                if (item.Value.setInfo == null)
                {
                    item.Value.setInfo  = new Setinfo();
                }
                item.Value.setInfo.Taged = tag.Taged;
                item.Value.setInfo.UserId = tag.UserId;
                item.Value.setInfo.UserName = tag.UserName;
                item.Value.setInfo.Description = tag.Description;
                item.Value.setInfo.TagedTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm");
                break;
            }
        }
    }
    /// <summary>
    /// 获取流程
    /// </summary>
    /// <returns></returns>
    public Flow ToSchemeObj()
    {
        flow.linkList = Lines;
        flow.nodeList = Nodes.Select(u => u.Value).ToList();
        return flow;
    }

    /// <summary>
    /// 通知三方系统，节点执行情况
    /// </summary>
    public void NotifyThirdParty(HttpClient client, Tag tag)
    {
        if (currentNode.setInfo == null || string.IsNullOrEmpty(currentNode.setInfo.ThirdPartyUrl))
        {
            return;
        }

        var postData = new
        {
            flowInstanceId,
            nodeName=currentNode.name,
            nodeId = currentNodeId,
            userId = tag.UserId,
            userName = tag.UserName,
            result=tag.Taged, //1：通过;2：不通过；3驳回
            description = tag.Description,
            execTime = tag.TagedTime,
            isFinish = currentNodeType == 4
        };
        HttpContent httpContent = new StringContent(postData.ToJsonString(), Encoding.UTF8);
        httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
        client.PostAsync(currentNode.setInfo.ThirdPartyUrl, httpContent);
    }

    #endregion 共有方法

    #region 属性
    /// <summary>
    /// 流程
    /// </summary>
    public Flow flow { get; set; }
    /// <summary>
    /// 运行实例的Id
    /// </summary>
    public long flowInstanceId { get; set; }

    /// <summary>
    /// 开始节点的ID
    /// </summary>
    public string startNodeId { get; set; }

    /// <summary>
    /// 当前节点的ID
    /// </summary>
    public string currentNodeId { get; set; }

    /// <summary>
    /// 当前节点类型 0会签开始,1会签结束,2一般节点,开始节点,4流程运行结束
    /// </summary>
    public int currentNodeType { get; set; }

    /// <summary>
    /// 当前节点的对象
    /// </summary>
    public FlowNode currentNode => Nodes[currentNodeId];

    /// <summary>
    /// 下一个节点
    /// </summary>
    public string nextNodeId { get; set; }

    /// <summary>
    /// 下一个节点类型 -1无法运行,0会签开始,1会签结束,2一般节点,4流程运行结束
    /// </summary>
    /// <value>The type of the next node.</value>
    public int nextNodeType { get; set; }

    /// <summary>
    /// 下一个节点对象
    /// </summary>
    public FlowNode nextNode => Nodes[nextNodeId];

    /// <summary>
    /// 上一个节点
    /// </summary>
    public string previousId { get; set; }

    /// <summary>
    /// 实例节点集合
    /// </summary>
    public Dictionary<string, FlowNode> Nodes { get; set; }

    /// <summary>
    /// 流程实例中所有的线段
    /// </summary>
    public List<FlowLine> Lines { get; set; }

    /// <summary>
    /// 从节点发出的线段集合
    /// </summary>
    public Dictionary<string, List<FlowLine>> FromNodeLines { get; set; }

    /// <summary>
    /// 到达节点的线段集合
    /// </summary>
    public Dictionary<string, List<FlowLine>> ToNodeLines { get; set; }

    /// <summary>
    /// 表单数据
    /// </summary>
    public string FrmData { get; set; }

    #endregion 属性
}
